library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity I2Cwatcher is
  Port(I2CData : in std_logic;
       I2CClk : in std_logic;
       Direction : out std_logic);
end I2Cwatcher;

architecture behav of I2Cwatcher is

  signal data,clk,dir : std_logic;
  type states is (idle,address,ack1,reg,ack2,read,write,ack3,stop);
  signal cs,ns : states:=idle;
  signal counter : std_logic_vector(3 downto 0);
  signal rw_bit : std_logic;

begin
  
  data<=I2CData;
  clk<=I2CClk;
  Direction<=dir;
  
  process(ns,clk)
  begin
    if falling_edge(clk) then
      cs<=ns;
    end if;
  end process;
  
  process(data,clk,cs,counter)
  begin
    
    case cs is
      when idle => 
        if clk = '1' and falling_edge(data) then
          ns<=address;
        end if;
        
      when address =>
        if counter = X"8" then
          ns<=ack1;
        end if;
        
      when ack1 =>
        if clk = '1' and data = '0' then
          ns<=reg;
        elsif falling_edge(clk) and data = '1' then 
          ns<=idle;
        end if;
        
      when reg =>
        if counter = X"8" then
          ns<=ack2;
        end if;
        
      when ack2 =>
        if clk = '1' and data = '0' then
          if rw_bit = '1' then
            ns<=read;
          else
            ns<=write;
          end if;
        elsif falling_edge(clk) and data = '1' then 
          ns<=idle;
        end if;
        
      when read =>
        if counter = X"8" then
          ns<=ack3;
        end if;
        
      when write =>
        if counter = X"8" then
          ns<=ack3;
        end if;
        
      when ack3 =>
        if clk = '1' and data = '0' then
          ns<=stop;
        elsif falling_edge(clk) and data = '1' then 
          ns<=stop;
        end if;
        
      when stop =>
        if clk = '1' and falling_edge(data) then
          ns<=address;
        elsif clk = '1' and rising_edge(data) then 
          ns<=idle;
        end if;
    end case;
  end process;
  
  
  -------------------------
  --Datapath
  -------------------------
  process(cs,clk)
  begin
    case cs is
      when idle => 
        counter<=X"0";
        dir<='0';
        
      when address =>
        dir<='0';
        if counter = X"8" then
          counter<=X"0";
        end if;
        if rising_edge(clk) then
          if counter = X"7" then
            rw_bit<=data;
          end if;
          counter<=counter + 1;
        end if;
        
      when ack1 => 
        dir<='1';
        
      when reg =>
        dir<='0';
        if counter = X"8" then
          counter<=X"0";
        end if;
        if rising_edge(clk) then
          counter<=counter + 1;
        end if;
        
      when ack2 =>
        dir<='1';
        
      when read =>
        dir<='1';
        if counter = X"8" then
          counter<=X"0";
        end if;
        if rising_edge(clk) then
          counter<=counter + 1;
        end if;
        
      when write =>
        dir<='0';
        if counter = X"8" then
          counter<=X"0";
        end if;
        if rising_edge(clk) then
          counter<=counter + 1;
        end if;
        
      when ack3 =>
        dir<= not dir;
        
      when stop =>
        dir<='0';
        
    end case;
  end process;
end behav;